use axum::{
    async_trait,
    extract::FromRequestParts,
    http::{header, request::Parts, StatusCode},
    Json,
};
use serde_json::{json, Value};

use crate::{
    facade::{builder::Builder, user::UserFacade},
    provider::user::UserProvider,
    system::error::ErrorCode, payload::user::UserInfoPayload,
};

#[derive(Clone, Debug)]
pub struct Auth {
    pub user: UserInfoPayload,
}
/**
 * where表示S泛化类型必须要有如下特性
 * Send 是允许在线程间转移所有权
 * sync 允许多线程访问
 */
#[async_trait]
impl<S> FromRequestParts<S> for Auth
where
    S: Send + Sync,
{
    type Rejection = (StatusCode, Json<Value>);
    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
        let auth_header = parts
            .headers
            .get(header::AUTHORIZATION)
            .and_then(|v| v.to_str().ok());
        // auth token 为none
        if auth_header.is_none() {
            return Err((
                StatusCode::OK,
                Json(json!(ErrorCode::UNAUTH
                    .to_web_ret()
                    .append_msg("auth_token_empty".to_string()))),
            ));
        }
        let token = auth_header.unwrap().to_string();
        let ret = UserProvider::builder().get_by_token(token);
        if ret.is_err() {
            return Err((
                StatusCode::OK,
                Json(json!(ErrorCode::UNAUTH
                    .to_web_ret()
                    .append_msg(ret.err().unwrap().to_string()))),
            ));
        }
        let auth_user_opt = ret.unwrap();
        if auth_user_opt.is_none() {
            return Err((
                StatusCode::OK,
                Json(json!(ErrorCode::UNAUTH
                    .to_web_ret()
                    .append_msg(String::from("user empty")))),
            ));
        }
        // success
        Ok(Self {
            user: auth_user_opt.unwrap(),
        })
    }
}
